<?php
/**
 * @link https://www.kancloud.cn/cleverstone/ymb2
 * @copyright Copyright (c) 2020 Yii Manager Software LLC
 */

namespace builder;

use builder\export\ExportBuilder;
use builder\helper\App;
use Yii;
use builder\menu\MenuBuilder;
use builder\base\Builder;
use builder\tab\TabBuilder;
use builder\form\FormBuilder;
use builder\form_step\FormStepBuilder;
use builder\lists\ListsBuilder;
use builder\tree\TreeBuilder;
use builder\table\TableBuilder;
use builder\tree_table\TreeTableBuilder;
use builder\card\CardBuilder;
use builder\base\ViewBuilderInterface;
use builder\base\InvalidInstanceException;
use builder\base\NotFoundParamsException;
use yii\base\BootstrapInterface;
use yii\base\InvalidArgumentException;
use yii\web\Application;

/**
 * 视图构建器
 *
 * @property-write $this $render                注册渲染项
 * @property-write $this $renders               批量注册渲染项
 * @property-read TableBuilder $table           表格构建器
 * @property-read TreeTableBuilder $treeTable   树表格构建器
 * @property-read FormBuilder $form             表单构建器
 * @property-read FormStepBuilder $formStep     分步表单构建器
 * @property-read ListsBuilder $lists           列表构建器
 * @property-read TabBuilder $tab               选项卡构建器
 * @property-read TreeBuilder $tree             树组件构建器
 * @property-read MenuBuilder $menu             菜单组件构建器
 * @property-read CardBuilder $card             卡片组件构建器
 * @property-read ExportBuilder $export         导出组件构建器
 * @property boolean $partial                   是否局部视图渲染
 * @property string $alignThemePath             视图布局排列主题路径
 *
 * @author cleverstone
 * @since ym2.0
 */
class ViewBuilder extends Builder implements BootstrapInterface
{
    /**
     * @var string 视图构建器版本号
     */
    public $version = '2.0.0';

    /**
     * @var array 要渲染的视图集合
     * - string 渲染HTML
     * - callable 可调用结构（\Closure | Function | Method）
     * - builder\base\Builder 构建器实例
     */
    private $_viewRenders = [];

    /**
     * @var bool 是否局部视图渲染
     * @see $partial
     * @see setPartial()
     * @see getPartial()
     */
    private $_partial = false;

    /**
     * @var string 视图布局排列主题路径
     * @see $alignThemePath
     * @see setAlignThemePath()
     * @see getAlignThemePath()
     */
    private $_alignThemePath = '';

    /**
     * 设置视图布局排列主题路径
     * @param string $themePath 视图布局排列主题路径
     * @return $this
     */
    public function setAlignThemePath($themePath)
    {
        $this->_alignThemePath = (string)$themePath;
        return $this;
    }

    /**
     * 获取布局排列主题视图路径
     * @return string
     */
    public function getAlignThemePath()
    {
        return $this->_alignThemePath;
    }

    /**
     * 设置是否局部视图渲染
     * @param bool $boolean
     * @return $this
     */
    public function setPartial($boolean = true)
    {
        $this->_partial = $boolean;
        return $this;
    }

    /**
     * @return bool
     */
    public function getPartial()
    {
        return $this->_partial;
    }

    /**
     * 构建器名称
     * @return string
     */
    public function getName()
    {
        return 'view';
    }

    /**
     * @return string the view path that may be prefixed to a relative view name.
     */
    public function getViewPath()
    {
        return '';
    }

    /**
     * 组件初始化
     * @param Application $app
     * @return void
     */
    public function bootstrap($app)
    {
        //注册文件上传控制器
        $app->controllerMap['__upload'] = [
            'class' => 'builder\uploads\UploadController',
        ];

        $app->setComponents([
            //注册文件上传管理器组件
            'uploadManager' => [
                'class' => 'builder\uploads\Upload',
                //上传本地驱动
                'provider' => App::params('app.upload.provider'),
            ],

        ]);

        $registerModules = [];
        if (YII_ENV_DEV) {
            //注册 视图构建器 测试模块
            $registerModules['ymtest'] = [
                'class' => \builder\test\Module::class,
            ];
        }
        $app->setModules($registerModules);

    }

    /**
     * 视图渲染
     * @return string
     * @throws InvalidInstanceException
     * @throws NotFoundParamsException
     */
    public function render()
    {
        if ($this->isAjax) {
            // Render AJAX
            $builderId = $this->builderId;
            if (empty($builderId)) {
                throw new NotFoundParamsException('Not found param：' . $this->idParam);
            }

            foreach ($this->_viewRenders as $builder) {
                if (is_callable($builder)) {
                    $builder = call_user_func($builder);
                }

                if (!$builder instanceof Builder) {
                    continue;
                }

                if (strcmp($builderId, $builder->id) === 0) {
                    return $builder->render();
                }
            }

            throw new InvalidArgumentException('The builder-id cannot be matched. ');
        } else {
            // Render HTML
            $result = [];
            foreach ($this->_viewRenders as $builder) {
                if (is_callable($builder)) {
                    $builder = call_user_func($builder);
                }

                if ($builder instanceof ViewBuilderInterface) {
                    // Render And Return String
                    $builder = $builder->render(true);
                }

                $this->throwExceptionIfNotString($builder);

                $result[] = $builder;
            }

            // 排列布局
            $content = implode("\n", $result);
            if (!empty($this->alignThemePath)) {
                $content = $this->_view->renderFile($this->alignThemePath, ['content' => $content], $this);
            }

            // 主布局
            return $this->_view->renderFile($this->partial ? $this->partialPath : $this->mainPath, ['content' => $content], $this);
        }
    }

    /**
     * 批量设置视图渲染项
     * ```php
     * $this->viewBuilder->renders = [
     *      function () {},
     *      string,
     *      Builder(Object),
     * ];
     * ```
     * @param array $renders
     * @return $this
     */
    public function setRenders(array $renders)
    {
        foreach ($renders as $render) {
            $this->setRender($render);
        }

        return $this;
    }

    /**
     * 设置视图渲染项
     * @param callable|Builder|string $render
     * @return $this
     */
    public function setRender($render)
    {
        $this->_viewRenders[] = $render;
        return $this;
    }

    /**
     * 获取表格构建器实例
     * @param array $config
     * @return TableBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getTable(array $config = [])
    {
        return $this->pushViewRender(TableBuilder::class, $config);
    }

    /**
     * 获取表单构建器实例
     * @param array $config
     * @return FormBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getForm(array $config = [])
    {
        return $this->pushViewRender(FormBuilder::class, $config);
    }

    /**
     * 获取分步表单构建器实例
     * @param array $config
     * @return FormStepBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getFormStep(array $config = [])
    {
        return $this->pushViewRender(FormStepBuilder::class, $config);
    }

    /**
     * 获取列表构建器实例
     * @param array $config
     * @return ListsBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getLists(array $config = [])
    {
        return $this->pushViewRender(ListsBuilder::class, $config);
    }

    /**
     * 获取选项卡构建器实例
     * @param array $config
     * @return TabBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getTab(array $config = [])
    {
        return $this->pushViewRender(TabBuilder::class, $config);
    }

    /**
     * 获取树组件构建器实例
     * @param array $config
     * @return TreeBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getTree(array $config = [])
    {
        return $this->pushViewRender(TreeBuilder::class, $config);
    }

    /**
     * 获取菜单组件构建器实例
     * @param array $config
     * @return MenuBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getMenu(array $config = [])
    {
        return $this->pushViewRender(MenuBuilder::class, $config);
    }

    /**
     * 获取树表格构建器实例
     * @param array $config
     * @return TreeTableBuilder
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getTreeTable(array $config = [])
    {
        return $this->pushViewRender(TreeTableBuilder::class, $config);
    }

    /**
     * 获取卡片构建器实例
     * @param array $config
     * @return mixed
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getCard(array $config = [])
    {
        return $this->pushViewRender(CardBuilder::class, $config);
    }

    /**
     * 获取导出构建器实例
     * @param array $config
     * @return mixed
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    public function getExport(array $config = [])
    {
        return $this->pushViewRender(ExportBuilder::class, $config);
    }

    /**
     * 追加构建器到视图渲染中
     * @param string $builderClass 构建器组件类名
     * @param array $initConfig
     * @return mixed
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\di\NotInstantiableException
     */
    protected function pushViewRender($builderClass, array $initConfig = [])
    {
        $builderInstance = Yii::$container->get($builderClass, [], $initConfig);
        $this->_viewRenders[] = $builderInstance;

        return $builderInstance;
    }
}